﻿using BulletSharp;
using BulletSharp.Math;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace ETModel {
    [ObjectSystem]
    public class BulletCollisionWorldAwakeSystem : AwakeSystem<BulletCollisionWorld> {
        public override void Awake(BulletCollisionWorld self) {
            self.Awake();
        }
    }
    [ObjectSystem]
    public class BulletCollisionWorldLateUpdateSystem : LateUpdateSystem<BulletCollisionWorld> {
        public override void LateUpdate(BulletCollisionWorld self) {
            self.LateUpdate();
        }
    }
    public class BulletCollisionWorld : Component, IDisposable {

        public static BulletCollisionWorld Instance { get; private set; }

        protected DefaultCollisionConfiguration CollisionConf;
        protected CollisionDispatcher Dispatcher;
        protected DbvtBroadphase Broadphase;
        /// <summary>
        /// 暂时先固定使用DiscreteDynamicsWorld
        /// </summary>
        protected DiscreteDynamicsWorld _world;
        public CollisionWorld World { get { return _world; } }


        Stopwatch frameTimer = new Stopwatch();
        

        /// <summary>
        /// 默认构造物理世界(DiscreteDynamicsWorld+重力方向为Y轴向下)
        /// </summary>
        public void Awake() {
            if (Instance != null) {
                throw new Exception("BulletCollisionWorld组件只能初始化一次,当然你想尝试一下多个物理世界你就要改代码了~");
            }
            Instance = this;
            CollisionConf = new DefaultCollisionConfiguration();
            Dispatcher = new CollisionDispatcher(CollisionConf);

            Broadphase = new DbvtBroadphase();

            _world = new DiscreteDynamicsWorld(Dispatcher, Broadphase, null, CollisionConf);
            _world.Gravity = new Vector3(0, -10, 0);
        }

        /// <summary>
        /// 释放所有资源
        /// </summary>
        public override void Dispose() {
            if (_world != null) {
                int i;
                //移除所有约束
                for (i = _world.NumConstraints - 1; i >= 0; i--) {
                    TypedConstraint constraint = _world.GetConstraint(i);
                    _world.RemoveConstraint(constraint);
                    constraint.Dispose();
                }

                //移除所有刚体并销毁,之后再看有其他碰撞体再加
                for (i = _world.NumCollisionObjects - 1; i >= 0; i--) {
                    CollisionObject obj = _world.CollisionObjectArray[i];
                    RigidBody body = obj as RigidBody;
                    if (body != null && body.MotionState != null) {
                        body.MotionState.Dispose();
                    }
                    _world.RemoveCollisionObject(obj);
                    obj.Dispose();
                }

                _world.Dispose();
                _world = null;
                Broadphase.Dispose();
                Broadphase = null;
                Dispatcher.Dispose();
                Dispatcher = null;
                CollisionConf.Dispose();
                CollisionConf = null;
            }
            if (Broadphase != null) {
                Broadphase.Dispose();
                Broadphase = null;
            }
            if (Dispatcher != null) {
                Dispatcher.Dispose();
                Dispatcher = null;
            }
            if (CollisionConf != null) {
                CollisionConf.Dispose();
                CollisionConf = null;
            }
            base.Dispose();

        }

        /*
        public virtual void FixedUpdate() {
            if (_world != null) {
                float deltaTime = _fixedTimeStep - _elapsedBetweenFixedFrames;
                int numSteps = _world.StepSimulation(deltaTime, 1, _fixedTimeStep);
                _frameCount += numSteps;
                _lastSimulationStepTime = TimeHelper.Now();
                _elapsedBetweenFixedFrames = 0f;
                _numUpdates = 0;
            }
        }
        //This is needed for rigidBody interpolation. The motion states will update the positions of the rigidbodies
        public virtual void Update() {
            float deltaTime = (TimeHelper.Now() - _lastSimulationStepTime) * 1f / 1000;
            if (deltaTime > _needTime && (_elapsedBetweenFixedFrames + deltaTime) < _fixedTimeStep) {
                _elapsedBetweenFixedFrames += deltaTime;
                int numSteps = _world.StepSimulation(deltaTime, 1, _fixedTimeStep);
                _frameCount += numSteps;
                _lastSimulationStepTime = TimeHelper.Now();
                _numUpdates++;
            }
        }
        */
        public virtual void LateUpdate() {
            _world.StepSimulation(GetFrameDelta());
        }
        public float GetFrameDelta() {
            float delta = (float)frameTimer.ElapsedTicks / Stopwatch.Frequency;
            frameTimer.Restart();
            return delta;
        }
    }
}
